﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace OpGen
{
    /// <summary>
    /// Mitteilungstypen
    /// </summary>
    public enum MessageTypeEnum : int
    {
        /// <summary>
        /// Ausnahme
        /// </summary>
        Fatal=5,

        /// <summary>
        /// Fehler
        /// </summary>
        Error = 4,

        /// <summary>
        /// Warnung
        /// </summary>
        Warning = 3,

        /// <summary>
        /// Normal
        /// </summary>
        Normal = 2,

        /// <summary>
        /// Debug Info
        /// </summary>
        Debug = 1,

        /// <summary>
        /// Detail Info
        /// </summary>
        Trace = 0
    }

    /// <summary>
    /// Callback Delegate das aufgerufen wird wenn eine Mitteilung erstellt wurde
    /// </summary>
    /// <param name="type">Mitteilungstyp</param>
    /// <param name="message">Mitteilung</param>
    public delegate void LoggerCallbackDelegate(MessageTypeEnum type, string message);
    
    /// <summary>
    /// Stellt Logger Funktionen bereit
    /// </summary>
    public static class Logger
    {
        /// <summary>
        /// Wird ausgelöst wenn eine neue Mitteilung erstellt wurde
        /// </summary>
        public static event LoggerCallbackDelegate LoggerCallback;

        /// <summary>
        /// Erstellt eine neue statische Instanz der Logger-Klasse
        /// </summary>
        static Logger()
        {
            MinMessageLevel = MessageTypeEnum.Normal;
            MaxMessageLevel = MessageTypeEnum.Error;
            LoggingEnabled = true;

            FatalColor = ConsoleColor.Magenta;
            ErrorColor = ConsoleColor.Red;
            WarningColor = ConsoleColor.Yellow;
            NormalColor = ConsoleColor.White;
            DebugColor = ConsoleColor.Gray;
            TraceColor = ConsoleColor.DarkGray;
        }

        private static object _threadLockObject = new object();

        private static bool _progressBar = false;
        private static int _progressBarRow = 0;
        private static int _progressBarMaxCount = 0;
        private static string _progressBarMessage = "";
        
        /// <summary>
        /// Gibt an ob der Logger aktiv ist
        /// </summary>
        public static bool LoggingEnabled { get; set; }

        /// <summary>
        /// Gibt den min. Mitteilungstyp an
        /// </summary>
        public static MessageTypeEnum MinMessageLevel { get; set; }

        /// <summary>
        /// Gibt den max. Mitteilungstyp an
        /// </summary>
        public static MessageTypeEnum MaxMessageLevel { get; set; }

        /// <summary>
        /// Gibt die Consolen-Schriftfarbe bei Ausnahmen an
        /// </summary>
        public static ConsoleColor FatalColor { get; set; }

        /// <summary>
        /// Gibt die Consolen-Schriftfarbe bei Fehlern an
        /// </summary>
        public static ConsoleColor ErrorColor { get; set; }

        /// <summary>
        /// Gibt die Consolen-Schriftfarbe bei Warnungen an
        /// </summary>
        public static ConsoleColor WarningColor { get; set; }

        /// <summary>
        /// Gibt die Consolen-Schriftfarbe bei Informationen an
        /// </summary>
        public static ConsoleColor NormalColor { get; set; }

        /// <summary>
        /// Gibt die Consolen-Schriftfarbe bei Debug-Informationen an
        /// </summary>
        public static ConsoleColor DebugColor { get; set; }

        /// <summary>
        /// Gibt die Consolen-Schriftfarbe bei Trace-Informationen an
        /// </summary>
        public static ConsoleColor TraceColor { get; set; }

        /// <summary>
        /// Erstellt eine neue Mittielung
        /// </summary>
        /// <param name="messageType">Mitteilungstyp</param>
        /// <param name="message">Mitteilungstext</param>
        /// <param name="args">Weitere Parameter</param>
        /// <remarks>Wenn eine Fortschrittsanzeige gestartet wurde, wird diese Beendet</remarks>
        public static void LogMessage(MessageTypeEnum messageType, string message, params object[] args)
        {
            if (LoggingEnabled)
            {
                lock (_threadLockObject)
                {
                    var fullMessage = string.Format(message, args);

                    if ((messageType >= MinMessageLevel && messageType <= MaxMessageLevel) | (messageType == MessageTypeEnum.Fatal))
                    {
                        // Wenn eine Progressbar angezeigt wird, diese Abbrechen...
                        if (_progressBar)
                        {
                            Console.Write(" [STOP]");
                            EndProgressBar();
                        }

                        // Farbe setzten
                        switch (messageType)
                        {
                            case MessageTypeEnum.Fatal: Console.ForegroundColor = FatalColor; break;
                            case MessageTypeEnum.Error: Console.ForegroundColor = ErrorColor; break;
                            case MessageTypeEnum.Warning: Console.ForegroundColor = WarningColor; break;
                            case MessageTypeEnum.Normal: Console.ForegroundColor = NormalColor; break;
                            case MessageTypeEnum.Debug: Console.ForegroundColor = DebugColor; break;
                            case MessageTypeEnum.Trace: Console.ForegroundColor = TraceColor; break;
                        }

                        Console.WriteLine(fullMessage);

                        if (LoggerCallback != null)
                            LoggerCallback.Invoke(messageType, message);
                    }
                }
            }
        }

        /// <summary>
        /// Erstellt eine neue Ausnahme Mitteilung
        /// </summary>
        /// <param name="message">Mitteilungstext</param>
        /// <param name="args">Weitere Parameter</param>
        /// <remarks>Wenn eine Fortschrittsanzeige gestartet wurde, wird diese Beendet</remarks>
        public static void Fatal(string message, params object[] args)
        {
            LogMessage(MessageTypeEnum.Fatal, message, args);
        }

        /// <summary>
        /// Erstellt eine neue Fehler Mitteilung
        /// </summary>
        /// <param name="message">Mitteilungstext</param>
        /// <param name="args">Weitere Parameter</param>
        /// <remarks>Wenn eine Fortschrittsanzeige gestartet wurde, wird diese Beendet</remarks>
        public static void Error(string message, params object[] args)
        {
            LogMessage(MessageTypeEnum.Error, message, args);
        }

        /// <summary>
        /// Erstellt eine neue Warnungs Mitteilung
        /// </summary>
        /// <param name="message">Mitteilungstext</param>
        /// <param name="args">Weitere Parameter</param>
        /// <remarks>Wenn eine Fortschrittsanzeige gestartet wurde, wird diese Beendet</remarks>
        public static void Warning(string message, params object[] args)
        {
            LogMessage(MessageTypeEnum.Warning, message, args);
        }

        /// <summary>
        /// Erstellt eine neue Informations Mitteilung
        /// </summary>
        /// <param name="message">Mitteilungstext</param>
        /// <param name="args">Weitere Parameter</param>
        /// <remarks>Wenn eine Fortschrittsanzeige gestartet wurde, wird diese Beendet</remarks>
        public static void Normal(string message, params object[] args)
        {
            LogMessage(MessageTypeEnum.Normal, message, args);
        }

        /// <summary>
        /// Erstellt eine neue Debug-Informations Mitteilung
        /// </summary>
        /// <param name="message">Mitteilungstext</param>
        /// <param name="args">Weitere Parameter</param>
        /// <remarks>Wenn eine Fortschrittsanzeige gestartet wurde, wird diese Beendet</remarks>
        public static void Debug(string message, params object[] args)
        {
            LogMessage(MessageTypeEnum.Debug, message, args);
        }

        /// <summary>
        /// Erstellt eine neue Trace.Informations Mitteilung
        /// </summary>
        /// <param name="message">Mitteilungstext</param>
        /// <param name="args">Weitere Parameter</param>
        /// <remarks>Wenn eine Fortschrittsanzeige gestartet wurde, wird diese Beendet</remarks>
        public static void Trace(string message, params object[] args)
        {
            LogMessage(MessageTypeEnum.Trace, message, args);
        }

        /// <summary>
        /// Startet eine Fortschrittsanzeige
        /// </summary>
        /// <param name="message">Mitteilung</param>
        /// <param name="maxCount">Anzahl der Einheiten</param>
        /// <param name="value">Aktueller Wert</param>
        public static void BeginProgressBar(string message, int maxCount, int value)
        {
            if (LoggingEnabled)
            {
                //Zeile weiterspringen wenn bereits eine Bar angezeigt wird
                if (_progressBar) Console.WriteLine();

                Console.ResetColor();

                _progressBar = true;
                _progressBarMaxCount = maxCount;
                _progressBarRow = Console.CursorTop;
                _progressBarMessage = message;

                UpdateProgressBar(value);
            }
        }

        /// <summary>
        /// Aktualisiert die Fortschirttsanzeige
        /// </summary>
        /// <param name="value">Aktueller Wert</param>
        public static void UpdateProgressBar(int value)
        {
            if (!_progressBar) return;

            lock (_threadLockObject)
            {
                float percent = ((float)value / (float)_progressBarMaxCount);
                if (percent > 1) percent = 1;

                var barCount = (int)(30 * percent);
                var barLine = new string('=', barCount+1);

                Console.SetCursorPosition(0, _progressBarRow);
                Console.Write("{0,-25} [{1,-30}] {2,3}% ({3}/{4})", _progressBarMessage, barLine, Convert.ToInt32(percent * 100f), value, _progressBarMaxCount);
            }
        }

        /// <summary>
        /// Beendet eine Fortschrittsanzeige
        /// </summary>
        public static void EndProgressBar()
        {
            if (!_progressBar) return;

            // Eine Zeile weiterspringen wenn bereits eine Bar angezeigt wird
            Console.WriteLine();

            _progressBar = false;
            _progressBarMaxCount = 0;
            _progressBarRow = 0;
            _progressBarMessage = "";
        }
    }
}
